TIP Sample App/TwitterSearchViewController.m (207 lines of code) (raw):

// // TwitterSearchViewController.m // TIP Sample App // // Created on 2/3/17. // Copyright © 2020 Twitter. All rights reserved. // #import <TwitterImagePipeline/TwitterImagePipeline.h> #import "AppDelegate.h" #import "TweetImageFetchRequest.h" #import "TwitterAPI.h" #import "TwitterSearchViewController.h" #import "ZoomingTweetImageViewController.h" @interface TweetWithMediaTableViewCell : UITableViewCell <TIPImageViewFetchHelperDataSource, TIPImageViewFetchHelperDelegate> @property (nonatomic) TweetInfo *tweet; - (instancetype)init; + (NSString *)reuseIdentifier; @end @interface TwitterSearchViewController () <UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource> @property (nonatomic, readonly) UISearchController *searchController; @property (nonatomic, readonly) UITableView *tableView; @end @implementation TwitterSearchViewController { NSString *_term; NSArray<TweetInfo *> *_tweets; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { self.navigationItem.title = @"Twitter Search"; } return self; } - (void)viewDidLoad { [super viewDidLoad]; _searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; _searchController.searchResultsUpdater = self; _searchController.definesPresentationContext = YES; _searchController.searchBar.delegate = self; [_searchController.searchBar sizeToFit]; _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _tableView.delegate = self; _tableView.dataSource = self; _tableView.tableHeaderView = _searchController.searchBar; [self.view addSubview:_tableView]; } #pragma mark - Table View - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return (NSInteger)_tweets.count; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; TweetImageInfo *imageInfo = _tweets[(NSUInteger)indexPath.row].images.firstObject; if (imageInfo) { ZoomingTweetImageViewController *vc = [[ZoomingTweetImageViewController alloc] initWithTweetImage:imageInfo]; [self.navigationController pushViewController:vc animated:YES]; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TweetInfo *tweet = _tweets[(NSUInteger)indexPath.row]; const BOOL hasImages = tweet.images.count > 0; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:(hasImages) ? [TweetWithMediaTableViewCell reuseIdentifier] : @"TweetNoMedia"]; if (!cell) { if (hasImages) { cell = [[TweetWithMediaTableViewCell alloc] init]; } else { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"TweetNoMedia"]; } } cell.textLabel.text = tweet.handle; cell.detailTextLabel.text = tweet.text; if (hasImages) { [(TweetWithMediaTableViewCell *)cell setTweet:tweet]; } return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return (_tweets[(NSUInteger)indexPath.row].images.count > 0) ? 180 : 44; } //- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath //{ // return (_tweets[(NSUInteger)indexPath.row].images.count > 0) ? 180 : 44; //} #pragma mark - Search Controller - (void)updateSearchResultsForSearchController:(UISearchController *)searchController { } #if TARGET_OS_MACCATALYST - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [self performSelector:@selector(_triggerSearch) withObject:nil afterDelay:0.5]; } #endif - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { [self _triggerSearch]; } - (void)_triggerSearch { UISearchBar *searchBar = self.searchController.searchBar; NSString *search = searchBar.text; _term = search; _searchController.active = NO; _searchController.searchBar.userInteractionEnabled = NO; searchBar.text = _term; [[TwitterAPI sharedInstance] searchForTerm:_term count:APP_DELEGATE.searchCount complete:^(NSArray<TweetInfo *> *tweets, NSError *error) { self->_tweets = [tweets copy]; self->_searchController.searchBar.userInteractionEnabled = YES; [self->_tableView reloadData]; }]; } - (void)willPresentSearchController:(UISearchController *)searchController { searchController.searchBar.text = _term; } @end @implementation TweetWithMediaTableViewCell { UIImageView *_tweetImageView; TIPImageViewFetchHelper *_tweetFetchHelper; } - (instancetype)init { if (self = [self initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:[[self class] reuseIdentifier]]) { // logic is decoupled from the View via a "Helper" object // Create our helper _tweetFetchHelper = [[TIPImageViewFetchHelper alloc] initWithDelegate:self dataSource:self]; // Create our image view _tweetImageView = [[UIImageView alloc] init]; _tweetImageView.tip_fetchHelper = _tweetFetchHelper; _tweetImageView.contentMode = UIViewContentModeScaleAspectFill; _tweetImageView.clipsToBounds = YES; _tweetImageView.backgroundColor = [UIColor lightGrayColor]; _tweetImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // Add image view to content [self.contentView addSubview:_tweetImageView]; } return self; } - (void)prepareForReuse { [super prepareForReuse]; self.tweet = nil; } - (void)setTweet:(TweetInfo *)info { _tweet = info; [_tweetFetchHelper clearImage]; [_tweetFetchHelper reload]; } - (void)layoutSubviews { [super layoutSubviews]; CGRect detailRect = self.detailTextLabel.frame; CGRect textRect = self.textLabel.frame; CGFloat yDelta = detailRect.origin.y - textRect.origin.y; textRect.origin.y = 3; detailRect.origin.y = textRect.origin.y + yDelta; self.detailTextLabel.frame = detailRect; self.textLabel.frame = textRect; CGRect imageRect = _tweetImageView.frame; imageRect.origin.y = detailRect.origin.y + detailRect.size.height + 3; imageRect.origin.x = detailRect.origin.x; imageRect.size.width = self.contentView.bounds.size.width - (2 * imageRect.origin.x); imageRect.size.height = self.contentView.bounds.size.height - (imageRect.origin.y + 3); _tweetImageView.frame = imageRect; } + (NSString *)reuseIdentifier { return @"TweetWithMedia"; } #pragma mark - data source - (nullable TIPImagePipeline *)tip_imagePipelineForFetchHelper:(nonnull TIPImageViewFetchHelper *)helper { return [APP_DELEGATE imagePipeline]; } - (nullable id<TIPImageFetchRequest>)tip_imageFetchRequestForFetchHelper:(nonnull TIPImageViewFetchHelper *)helper { TweetImageInfo *tweetImage = _tweet.images.firstObject; if (!tweetImage) { return nil; } TweetImageFetchRequest *request = [[TweetImageFetchRequest alloc] initWithTweetImage:tweetImage targetView:helper.fetchView]; request.forcePlaceholder = APP_DELEGATE.usePlaceholder; return request; } #pragma mark - delegate - (BOOL)tip_fetchHelper:(nonnull TIPImageViewFetchHelper *)helper shouldUpdateImageWithPreviewImageResult:(nonnull id<TIPImageFetchResult>)previewImageResult { return YES; } - (BOOL)tip_fetchHelper:(nonnull TIPImageViewFetchHelper *)helper shouldContinueLoadingAfterFetchingPreviewImageResult:(nonnull id<TIPImageFetchResult>)previewImageResult { if (previewImageResult.imageIsTreatedAsPlaceholder) { return YES; } id<TIPImageFetchRequest> request = helper.fetchRequest; if ([request respondsToSelector:@selector(options)] && (request.options & TIPImageFetchTreatAsPlaceholder)) { // would be a downgrade, stop return NO; } const CGSize originalDimensions = previewImageResult.imageOriginalDimensions; const CGSize viewDimensions = TIPDimensionsFromView(helper.fetchView); if (originalDimensions.height >= viewDimensions.height && originalDimensions.width >= viewDimensions.width) { return NO; } return YES; } - (BOOL)tip_fetchHelper:(nonnull TIPImageViewFetchHelper *)helper shouldLoadProgressivelyWithIdentifier:(nonnull NSString *)identifier URL:(nonnull NSURL *)URL imageType:(nonnull NSString *)imageType originalDimensions:(CGSize)originalDimensions { return YES; } //- (BOOL)tip_fetchHelper:(nonnull TIPImageViewFetchHelper *)helper shouldReloadAfterDifferentFetchCompletedWithImageContainer:(nonnull TIPImageContainer *)imageContainer dimensions:(CGSize)dimensions identifier:(nonnull NSString *)identifier URL:(nonnull NSURL *)URL treatedAsPlaceholder:(BOOL)placeholder manuallyStored:(BOOL)manuallyStored //{ // //} @end